import java.util.ArrayList;
import java.util.Arrays;
import java.util.Random;

/**
 * The main class for simulation.
 * @author davnils 
 */
class Simulator {
	private int speed;
	private ArrayList<Bot> bots;
	private long prevStepTime;
	private ArrayList<String> fileNames;
	int [] botStats;
	boolean running;

	/**
	 * Initializes some fields and begins the parsing of bots.
	 * @param fileNames the filenames of the bots to load, without 
	 * folder prefix. "Bots/" is assumed to be the folder.
	 */
	public Simulator(ArrayList<String> fileNames) {
		speed = 3;
		bots = new ArrayList<Bot>();
		prevStepTime = 0;
		this.fileNames = fileNames;
		running = false;

		parseBots();
	}

	/**
	 * Creates the bots and stats.
	 */
	private void parseBots()
	{
		for(int i = 0; i < fileNames.size(); i++) {
			try {
				Bot bot = new Bot(fileNames.get(i), i + 1);
				bots.add(bot);
			}
			catch(Exception e) {
				System.out.println("Removed bot, Bot-constructor failed");
				fileNames.remove(i);
			}
		}
		botStats = new int[fileNames.size()];
	}

	/**
	 * Resets the simulation.
	 */
	public void reset() {
		this.speed = 5;
		GameMap.clearMap();
		Arrays.fill(botStats, 0);
		running = false;

		for(Bot bot : bots) {
			bot.resetRegisters();
		}
	}

	/**
	 * Change the current simulation speed to "speed" steps per second.
	 * @param speed the number of steps per second to be simulate.
	 */
	public void changeSpeed(int speed)
	{
		this.speed = speed;
	}

	/**
	 * Runs one step of the simulation. Each bots direction is updated, 
	 * moved and checked for collisions.
	 */
	public void simulateOneStep() {
		if(!running) {
			System.out.println("Starting simulation with " + fileNames.size() + " bots");
			running = true;

			int x, y;
			Random rng = new Random();
			for(int i = 0; i < fileNames.size(); i++) {
				do {
					x = rng.nextInt(GameMap.getSideLength() - 2) + 1;
					y = rng.nextInt(GameMap.getSideLength() - 2) + 1;
				} while(GameMap.getPosition(x, y) != 0);
				GameMap.setPosition(x, y, -(i + 1));
			}
		}
		if(speed<=10){
			while(!(System.currentTimeMillis() - prevStepTime > (1000.0f / speed))){
			}
			prevStepTime = System.currentTimeMillis();
		}
		
		
		for(Bot bot : bots) {
			int playerNumber = bot.getPlayerNumber();
			if(botStats[playerNumber-1] != 0) {
				continue;
			}

			int [] pos = GameMap.getPlayerPosition(playerNumber);
			GameMap.Direction dir = bot.getDirection();
			GameMap.setPosition(pos[0], pos[1], playerNumber);

			if(dir == GameMap.Direction.UP) {
				pos[1]--;
			}
			else if(dir == GameMap.Direction.DOWN) {
				pos[1]++;
			}
			else if(dir == GameMap.Direction.RIGHT) {
				pos[0]++;
			}
			else if(dir == GameMap.Direction.LEFT) {
				pos[0]--;
			}

			if(GameMap.getPosition(pos[0], pos[1]) != 0) {
				System.out.println("Bot " + playerNumber + " crashed");
				int max = botStats[0];
				for(int i = 0; i < botStats.length; i++) {
					if(botStats[i] > max) {
						max = botStats[i];
					}
				}
				if(max + 1 == botStats.length) {
					running = false;
					System.out.println("Round finished");
				}
				botStats[playerNumber-1] = max + 1;
			}
			else{
				GameMap.setPosition(pos[0], pos[1], -playerNumber);
			}

		}
	}

	/**
	 * Returns the current simulation statistics.
	 * The array is indexed after player IDs.
	 * A value of zero indicates that the bot is alive, 
	 * any other value indicates its final position.
	 * @return array with stats.
	 */
	public int [] getStats() {
		return botStats;
	}

	/**
	 * Returns an array of bots.
	 * @return array of bots.
	 */
	public ArrayList<Bot> getPlayers()
	{
		return bots;
	}
}
